openSUSE:Build Service cross distribution howto
Overview
This howto lists special hints to work with one spec file for different distributions. It is not a beginners guide for packagers, please visit the Build Service Tutorial for this instead. Also, this document does not cover distributions that use Debian packages.
The Build Service can reliably package rpms for not only openSUSE, but also recent SLES, CentOS, Fedora, Red Hat Enterprise Linux, Ubuntu, Debian and Mandriva/Mageia distributions.
What are the limitations? The only practical limit is not having certain dependencies satisfied for Fedora or Mandriva, where they can be easily satisfied for SUSE distributions by linking or aggregating to another Build Service project. An example: It is easy to build packages for SUSE which require the latest versions of Qt4 or GTK2, as these dependencies can be satisfied by other repos on the Build Service which provide backports of newer Qt4 packages for older SUSE versions, as well as SLES. This can only be overcome by also packaging the needed dependencies within your own project. Even then, this is only an issue if you wish to support as many distributions as possible.
Some 'gotcha' differences between distributions which you need to pay attention to:
- Installing desktop files. Each of the five distros use somewhat different mechanisms for installing desktop files and then creating the menu entries. Mandriva even has a special rpm macro %update_menus to accomplish this, which uses a Debian-like setup, different from SUSE or Fedora. Example:
%post %update_menus %postun %clean_menus
- Package naming for dependencies. See below for more specific hints and tricks to make this work in your spec file.
- Subtle differences in rpm macros. Link with a table showing the variations below.
Can you point me to good example of a cross-platform package ?
Detect a distribution flavor for special code
You can add
%if %{defined suse_version} %if %{undefined suse_version}
or more portable:
%if 0%{?suse_version} <suse stuff here> %else <other distros> %endif
to check if the current build is for a SUSE distribution or not. Note that [there is no '%elseif' to chain multiple tests at the same level. Note that Fedora uses "%{fedora}" without "_version". You can also check for which SUSE distribution by using
%if 0%{?suse_version} > 1130
for instance to execute something for everything after SUSE Linux 11.3. Similar checks for other distributions exist as well.
%if %{defined fedora} %if %{defined mdkversion} %if 0%{?fedora} < 5 %if 0%{?mdkversion} > 2006
Note that rpm prefers:
>=
vs.
=>
You can also exclude one particular version:
%if 0%{?rhel_version} != 406
You can combine multiple distributions in a single if block like this:
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version} <yourstuff> %endif
To group conditionals, split the conditional to several lines, like this:
# mono: only on suse, sle only from sle10 %if 0%{?suse_version} %if 0%{?suse_version} >= 1010 BuildRequires: mono-core %endif %endif
Another method is to use parentheses, for example:
# mono: only on suse, sle only from sle10 %if 0%{?suse_version} && ( 0%{?suse_version} == 0 || 0%{?suse_version} >= 1010 ) BuildRequires: mono-core %endif
Grouping with parentheses might fail on older RHEL-based distributions like Fedora, RHEL and CentOS. Anything RHEL-5-based and higher should support grouping. For older versions, split up your conditions and nest them properly.
You can also select a special architecture using e.g.:
%if 0%{?suse_version} == 1310 %ifarch x86_64 <yourstuff> %endif %endif
Warning: The versions listed below can change! To check for the latest version, go to the project page and check the configuration file (e.g. for RHEL 5, look for the content "%rhel_version": https://build.opensuse.org/project/prjconf?project=RedHat%3ARHEL-5). Please update this page if versions doesn't match.
Note: The "sles_version" macro is no longer set in SLE 12.
openSUSE:Packaging_for_Leap#RPM_Distro_Version_Macros help to choose the right settings for openSUSE/SUSE. One can also use osc buildconfig
to check macro definitions for a specific build configuration.
Locally you can also run the following to get your value:
grep suse_version /usr/lib/rpm/suse/macros
Otherwise the following table may help to choose correct settings, including non-(open)SUSE distros:
Distribution | Variable | Comment |
---|---|---|
openSUSE Tumbleweed | %if 0%{?suse_version} > 1600 | current upcoming release (changing) |
openSUSE Leap 16.0 | %if 0%{?suse_version} == 1600 && 0%{?is_opensuse} | SLES 16 currently doesn't use the sle_version macro. |
openSUSE Leap 15.6 | %if 0%{?sle_version} == 150600 && 0%{?is_opensuse} | |
openSUSE Leap 15.5 | %if 0%{?sle_version} == 150500 && 0%{?is_opensuse} | |
openSUSE Leap 15.4 | %if 0%{?sle_version} == 150400 && 0%{?is_opensuse} | |
openSUSE Leap 15.3 | %if 0%{?sle_version} == 150300 && 0%{?is_opensuse} | |
openSUSE Leap 15.2 | %if 0%{?sle_version} == 150200 && 0%{?is_opensuse} | |
openSUSE Leap 15.1 | %if 0%{?sle_version} == 150100 && 0%{?is_opensuse} | |
openSUSE Leap 15.0 | %if 0%{?sle_version} == 150000 && 0%{?is_opensuse} | could also be Backports:SLE-15¹ |
openSUSE Leap 42.3 | %if 0%{?sle_version} == 120300 && 0%{?is_opensuse} | "leap_version" is deprecated, see below |
openSUSE Leap 42.2 | %if 0%{?sle_version} == 120200 && 0%{?is_opensuse} | "leap_version" is deprecated, see below |
openSUSE Leap 42.1 | %if 0%{?sle_version} == 120100 && 0%{?is_opensuse} | |
openSUSE Leap 42.x | %if 0%{?suse_version} == 1315 && 0%{?is_opensuse} | use comparisons above to distinguish between specific versions |
openSUSE 13.2 | %if 0%{?suse_version} == 1320 | |
openSUSE 13.1 | %if 0%{?suse_version} == 1310 | |
openSUSE 12.3 | %if 0%{?suse_version} == 1230 | |
openSUSE 12.2 | %if 0%{?suse_version} == 1220 | |
openSUSE 12.1 | %if 0%{?suse_version} == 1210 | |
openSUSE 11.4 | %if 0%{?suse_version} == 1140 | |
openSUSE 11.3 | %if 0%{?suse_version} == 1130 | |
openSUSE 11.2 | %if 0%{?suse_version} == 1120 | |
openSUSE 11.1 | %if 0%{?suse_version} == 1110 | could also be SLE11* |
openSUSE 11.0 | %if 0%{?suse_version} == 1100 | |
openSUSE 10.3 | %if 0%{?suse_version} == 1030 | |
openSUSE 10.2 | %if 0%{?suse_version} == 1020 | |
SUSE Linux 10.1 | %if 0%{?suse_version} == 1010 | could also be SLE10* |
SUSE Linux 10.0 | %if 0%{?suse_version} == 1000 | |
SUSE Linux 9.3 | %if 0%{?suse_version} == 930 | |
Backports:SLE-15 | %if 0%{?sle_version} == 150000 && 0%{?is_backports} | |
Backports:SLE-12-SP5 | %if 0%{?sle_version} == 120500 && 0%{?is_backports} | |
Backports:SLE-12-SP4 | %if 0%{?sle_version} == 120400 && 0%{?is_backports} | |
Backports:SLE-12-SP3 | %if 0%{?sle_version} == 120300 && 0%{?is_backports} | |
Backports:SLE-12-SP2 | %if 0%{?sle_version} == 120200 && 0%{?is_backports} | |
Backports:SLE-12-SP1 | %if 0%{?sle_version} == 120100 && 0%{?is_backports} | |
Backports:SLE-12 | %if 0%{?sle_version} == 120000 && 0%{?is_backports} | |
SLE 15 SP4 | %if 0%{?sle_version} == 150400 && !0%{?is_opensuse} | |
SLE 15 SP3 | %if 0%{?sle_version} == 150300 && !0%{?is_opensuse} | |
SLE 15 SP2 | %if 0%{?sle_version} == 150200 && !0%{?is_opensuse} | |
SLE 15 SP1 | %if 0%{?sle_version} == 150100 && !0%{?is_opensuse} | |
SLE 15 GA | %if 0%{?sle_version} == 150000 && !0%{?is_opensuse} | |
SLE 12 SP5 | %if 0%{?sle_version} == 120500 && !0%{?is_opensuse} | |
SLE 12 SP4 | %if 0%{?sle_version} == 120400 && !0%{?is_opensuse} | |
SLE 12 SP3 | %if 0%{?sle_version} == 120300 && !0%{?is_opensuse} | |
SLE 12 SP2 | %if 0%{?sle_version} == 120200 && !0%{?is_opensuse} | |
SLE 12 SP1 | %if 0%{?sle_version} == 120100 && !0%{?is_opensuse} | |
SLE 12 GA | %if 0%{?sle_version} == 120000 && !0%{?is_opensuse} | |
SLE 12 | %if 0%{?suse_version} == 1315 && !0%{?is_opensuse} | remove "is_opensuse" check to get both Leap 42 and SLE 12 |
SLE 11 | %if 0%{?sles_version} == 11 | can also check that "suse_version" is 1110 |
SLE 10 | %if 0%{?sles_version} == 10 | can also check that "suse_version" is 1010 |
SLES 9 | %if 0%{?sles_version} == 9 | can also check that "suse_version" is 910 |
CentOS 5 | %if 0%{?centos_version} == 505 | |
CentOS 6 | %if 0%{?centos_version} == 600 | |
CentOS 7 | %if 0%{?centos_version} == 700 | |
RHEL 4 | %if 0%{?rhel_version} == 406 | |
RHEL 5 | %if 0%{?rhel_version} == 505 | |
RHEL 6 | %if 0%{?rhel_version} == 600 | |
RHEL 7 | %if 0%{?rhel_version} == 700 | |
RHEL 8 | %if 0%{?rhel} == 8 | |
Scientific Linux 6 | %if 0%{?scientificlinux_version} == 600 | |
Scientific Linux 7 | %if 0%{?scientificlinux_version} == 700 | |
Fedora 6 with Extras | %if 0%{?fedora_version} == 6 | |
Fedora 7 with Extras | %if 0%{?fedora_version} == 7 | |
Fedora 8 with Extras | %if 0%{?fedora_version} == 8 | |
Fedora 9 with Extras | %if 0%{?fedora_version} == 9 | |
Fedora 10 with Extras | %if 0%{?fedora_version} == 10 | |
Fedora 11 with Extras | %if 0%{?fedora_version} == 11 | |
Fedora 15 | %if 0%{?fedora_version} == 15 | |
Fedora 16 | %if 0%{?fedora_version} == 16 | |
Fedora 17 | %if 0%{?fedora_version} == 17 | |
Fedora 18 | %if 0%{?fedora_version} == 18 | |
Fedora 19 | %if 0%{?fedora_version} == 19 | |
Fedora 20 | %if 0%{?fedora_version} == 20 | |
Fedora 21 | %if 0%{?fedora_version} == 21 | |
Fedora 36 | %if 0%{?fedora} == 36 | The fedora_version macro is not available. |
Mandriva 2006 | %if 0%{?mdkversion} == 2006 | |
Mandriva 2007 | %if 0%{?mdkversion} == 2007 | |
Mandriva 2008 | %if 0%{?mdkversion} == 2008 | |
Mandriva 2009.0 | %if 0%{?mdkversion} == 2009 | |
Mandriva 2009.1 | %if 0%{?mdkversion} == 200910 | |
Mandriva 2010.0 | %if 0%{?mdkversion} == 201000 | |
Arch Linux | %if 0%{?arch_linux} | Only works in project configuration of course |
Debian | %if 0%{?debian} | Only works in project configuration of course |
* Use 0%{?is_opensuse} to avoid conflicts with SLE versions
¹ Use %if !0%{?is_backports} to differentiated between Leap 15 and Backports:SLE-15
leap_version macro (deprecated)
The "leap_version" macro is now deprecated and has been removed from Leap 15.0.
This macro should not be used for new RPM spec files: use the "sle_version" and "is_opensuse" macros together instead.
See table below for possible values. If a distribution is not listed, the macro is not defined.
Distribution | Value |
---|---|
openSUSE Leap 42.1 | NOT DEFINED |
openSUSE Leap 42.2 | 420200 |
openSUSE Leap 42.3 | 420300 |
openSUSE Leap 15.0 | NOT DEFINED |
ServicePacks can only be distinguished for SLES12. SLES11 SP1 sets exactly the same variables as SLES11.
You should also have a look for Leap/SLES here: openSUSE:How_to_contribute_to_Leap
Install info files
Info files should get installed by using the %info_add and %info_del macros. For example
%post %info_add %{name}.info
%preun %info_del %{name}.info
Please note that the info files get compressed on some distros as .gz and as .bz2 or even .lzma (recent Mandriva/Mageia). You can use %ext_info for the file suffix in the file list.
Install man files
The man files are compressed on some distros as .gz and as .bz2 or even .lzma (recent Mandriva/Mageia). You can use %ext_man for the file suffix in the file list.
The macros for ext_info & ext_man are not defined for Mageia or Fedora, so you will need to add them to the prjconf.
%if 0%{?mageia} == 6 Macros: %ext_info .xz %ext_man .xz :Macros %endif %if 0%{?fedora_version} && 0%{?fedora_version} >= 25 Macros: %ext_info .gz %ext_man .gz :Macros %endif
Handling dependencies
Different distributions often use different names for packages, so the Requires: and BuildRequires: tags may need to vary from repository to repository. It is possible to specify package name substitutions in the configuration of a project e.g.
%if 0%{?fedora_version} Substitute: libnetcdf-devel netcdf %endif
This will "replace" libnetcdf-devel by netcdf in all affected specfiles.
In order to change your project configuration, use osc meta prjconf <project-name> -e, which starts $EDITOR. In the web client this is hidden in the Advanced section of the central project menu.
In case you are doing this on build.opensuse.org and you think your substitution is of interest to others as well (and might be added to the central configuration) drop a message on opensuse-buildservice@opensuse.org.
If you want to have a look which Substitutes for a special distro already exist, have a look at the project configuration of that distro, e.g. https://build.opensuse.org/project/prjconf?project=CentOS%3ACentOS-6
Recommends: and Suggests:
Suggests: is a feature available in SUSE (>=10) and Fedora (>=24). This is a weak version of "Requires:".
# the package does not need vi or graphviz to run. %if 0%{?suse_version} >= 1000 Suggests: vim graphviz %endif
The same applies to Recommends:
update-alternatives package is SUSE only
update-alternatives is a SUSE package, but not available on Fedora/RHEL, where the functionality provided by the chkconfig package. Solution: just use the full path to the binary:
Requires(post): /usr/sbin/update-alternatives Requires(postun): /usr/sbin/update-alternatives
instead of
# Requires(post): update-alternatives # Requires(postun): update-alternatives
The other solution is to add the following to the project configuration:
%if ! 0%{?suse_version} Substitute: update-alternatives chkconfig %endif
Finding Qt 3.x on Fedora/RHEL
Redhat/Fedora uses a different naming scheme and build setup to use Qt to build other apps. Here is an example from a spec file to build Qt based apps on both SUSE and Fedora in one spec file:
BuildRequires: cups cups-devel python-devel shared-mime-info libart_lgpl-devel libtiff-devel libxml2-devel BuildRequires: fontconfig-devel openssl-devel pkgconfig desktop-file-utils qt-devel %if 0%{?fedora} >= 5 BuildRequires: libstdc++-devel gcc-c++ lcms-devel >= 1.12 qt %endif %if 0%{?suse_version} > 910 BuildRequires: update-desktop-files %endif
Then:
%if 0%{?fedora} >= 5 source "%{_sysconfdir}/profile.d/qt.sh" %endif %configure \ %if 0%{?fedora} >= 5 --with-xinerama \ --with-extra-libs=%{_libdir} \ %endif %if 0%{?suse_version} > 910 --with-qt-libraries=/usr/%_lib/qt3/%_lib \ --with-docdir=%{prefix}/share/doc/packages/scribus \ %ifarch x86_64 ppc64 s390x --enable-libsuffix=64 \ %endif %endif <programoptions>
Finding Qt4 on Mandriva
By default, Qt3 will be found before Qt4, so simply add this to the %build section of your spec file:
%build %if 0%{?mandriva_version} > 2006 export PATH=/usr/lib/qt4/bin:$PATH export QTDIR=%{_prefix}/lib/qt4/ %endif
- This may have changed on Mdv 2008
Handling Mandriva Provides/Obsoletes
Special care is needed when handling Mandriva, as unlike CentOS, RHEL, Fedora or SUSE, they often create separate library rpms for many apps which use these libraries privately. Application foo may also have a dependency on libfoo or libfoo-0. Thus, you need to check Mandriva original spec files if they have explicit Provides and Obsoletes to prevent conflicts on install. Even though RPM handles dependencies automatically, you may still need to explicitly added these like below:
%if 0%{?mandriva_version} Provides: foo libfoo-0 Obsoletes: foo libfoo-0 %endif
Building on Older Distros
While sources in a package can build on older distros, the way packages are built sometimes evolve in an incompatible way.
For example, some spec files sometimes call `autoreconf -fi`, it is possible that the tarball's versions of the config.sub, config.guess and ltmain.sh files are replaced with the copies that live in the autoconf package of the build host as a result of the -i option. When the build host software is sufficiently old and the to-be-built software sufficiently modern, this can cause a downgrade of the config files, which may have negative effects, and even lead to a miscompile, for example when building libapr1 on SLE10.
Thus, it might be better, or even required, that you call autoheader, autoconf or whatever is needed by hand instead of autoreconf on targets older than Factory. An example would be:
%if 0%{?suse_version} > 1010 autoreconf -fi %else autoheader autoconf %endif
Debian and xUbuntu packages
For these kind of packages, please read the debian builds page.
CentOS and RHEL EPEL repositories
For CentOS and RHEL you might want to use the EPEL repositories. They are available in OBS at Fedora:EPEL:7
Arch Linux packaging guides
Creating packages and Arch packaging standards
Distro RPM Macros and Documentation
openSUSE Packaging Conventions